package main

import (
	"archive/zip"
	"config-client-v1/init/global"
	j_log "config-client-v1/init/j-log"
	"config-client-v1/model"
	"config-client-v1/utils/tools"
	"encoding/json"
	"fmt"
	"gorm.io/gorm"
	"io"
	"os"
	"path/filepath"
	"strings"
	"time"
)

func main() {
	//projectExport()
	//projectImport()
}

// Unzip 将 zip 文件解压到目标目录
func Unzip(zipFile, dest string) error {
	reader, err := zip.OpenReader(zipFile)
	if err != nil {
		return err
	}
	defer reader.Close()
	if err := os.MkdirAll(dest, 0755); err != nil {
		return err
	}
	for _, file := range reader.File {
		err := extractFile(file, dest)
		if err != nil {
			return err
		}
	}
	return nil
}

// extractFile 解压单个文件或目录
func extractFile(file *zip.File, dest string) error {
	// 构建目标路径
	filePath := filepath.Join(dest, file.Name)

	// 防止路径遍历攻击（如: ../../../etc/passwd）
	if !isValidPath(filePath, dest) {
		return fmt.Errorf("无效路径，可能路径遍历攻击: %s", filePath)
	}

	// 如果是目录，创建它
	if file.FileInfo().IsDir() {
		return os.MkdirAll(filePath, file.Mode())
	}

	// 确保父目录存在
	if err := os.MkdirAll(filepath.Dir(filePath), 0755); err != nil {
		return err
	}

	// 打开压缩包中的文件
	rc, err := file.Open()
	if err != nil {
		return err
	}
	defer rc.Close()

	// 创建目标文件
	dstFile, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, file.Mode())
	if err != nil {
		return err
	}
	defer dstFile.Close()

	// 复制内容
	_, err = io.Copy(dstFile, rc)
	return err
}

// isValidPath 防止路径遍历（如: ../../ 越界）
func isValidPath(filePath, dest string) bool {
	rel, err := filepath.Rel(dest, filePath)
	if err != nil {
		return false
	}
	return !strings.HasPrefix(rel, "..")
}

func projectImport() {
	param := struct {
		File string `json:"file" binding:"required"`
	}{}
	param.File = "tmp/1759128223491201900.zip"
	param.File = filepath.Join(global.StaticPath, param.File)
	exist, err := tools.FileExist(param.File)
	if err != nil {
		j_log.LogError([]interface{}{"打开文件失败", err})
		return
	}
	if !exist {
		j_log.LogError([]interface{}{"文件不存在", param.File})
		return
	}
	destDIR := filepath.Join(global.StaticPath, "tmp", tools.GetNano())
	err = Unzip(param.File, destDIR)
	if err != nil {
		j_log.LogError([]interface{}{"文件解压失败", err})
		return
	}

	jsonFileContent, err := tools.ReadFileContent(filepath.Join(destDIR, "project.zt"))
	if err != nil {
		j_log.LogError([]interface{}{"读取工程文件失败", err})
		return
	}
	project := model.ProjectExt{}
	err = json.Unmarshal([]byte(jsonFileContent), &project)
	if err != nil {
		j_log.LogError([]interface{}{"解析工程文件失败", err})
		return
	}
	rawProjectID := project.ID
	j_log.LogInfo([]interface{}{"工程文件解析成功, 开始导入"})
	projectExist := model.Project{}
	err = model.DB.Where("name = ?", project.Name).Limit(1).Find(&projectExist).Error
	if err != nil {
		j_log.LogError([]interface{}{"sql error", err})
		return
	}

	now := tools.GetNow()
	if projectExist.ID != 0 {
		j_log.LogInfo([]interface{}{"工程名称已存在, 准备覆盖旧工程:", project.Name})
		if projectExist.ID == model.RunningProject.ID {
			j_log.LogError([]interface{}{"当前工程正在运行, 不允许覆盖"})
			return
		}
		err = model.DeleteProject(projectExist.ID)
		if err != nil {
			j_log.LogError([]interface{}{"DeleteProject error", err})
			return
		}
		j_log.LogInfo([]interface{}{"旧工程已删除", project.Name})
	}

	startTime := time.Now()
	j_log.LogInfo([]interface{}{"工程名称不存在, 准备创建新工程:", project.Name})
	project.ID = 0
	err = model.DB.Transaction(func(tx *gorm.DB) error {
		project.UID = 0
		project.CreateTime = &now
		project.UpdateTime = &now
		err = tx.Select("name", "desc", "server_name", "server_ip", "server_port", "time_ss", "redundant_enable", "redundant_name",
			"redundant_ip", "redundant_port", "redundant_time_ss", "create_time", "update_time", "api_url", "ws_url").Create(&project).Error
		if err != nil {
			return err
		}
		for _, ppe := range project.PPEList {
			ppe.ID = 0
			ppe.ProjectID = project.ID
			project.CreateTime = &now
			project.UpdateTime = &now
			err = tx.Select("project_id", "code1", "code2", "code3", "protocol_name", "mode",
				"name", "config", "sort", "create_time", "update_time").Create(ppe).Error
			if err != nil {
				return err
			}
			for _, frame := range ppe.FrameList {
				frame.ID = 0
				frame.ProjectID = project.ID
				frame.CreateTime = &now
				frame.UpdateTime = &now
				err = tx.Select("project_id", "code1", "code2", "code3", "ppe_id", "name", "frame_type", "frame_mode",
					"frame_format", "frame_addr", "dec_type", "length", "send_type", "slave_id", "function_code", "db_number",
					"start_addr", "quantity", "data_mode", "json_detail", "data_content", "trigger_condition", "trigger_relation", "trigger_interval",
					"status", "trigger_var", "trigger_type", "related_frame_id", "create_time", "update_time").Create(frame).Error
				if err != nil {
					return err
				}
				itemList := make([]*model.FrameItem, 0)
				for _, item := range frame.FrameItem {
					item.ID = 0
					item.ProjectID = project.ID
					item.PPEID = ppe.ID
					item.FrameID = frame.ID
					item.CreateTime = &now
					item.UpdateTime = &now
					itemList = append(itemList, item)
				}
				if len(itemList) > 0 {
					err = tx.Create(itemList).Error
					if err != nil {
						return err
					}
				}
			}
			j_log.LogInfo([]interface{}{"已导入第三方协议设备:", ppe.Name})
		}
		for _, fse := range project.FSEList {
			fse.ID = 0
			fse.ProjectID = project.ID
			project.CreateTime = &now
			project.UpdateTime = &now
			err = tx.Select("project_id", "name", "code1", "ip", "port", "redundant_enable",
				"redundant_ip", "redundant_port", "redundant_param1", "redundant_param2", "redundant_param3",
				"int_endian", "float_endian", "detail", "sort", "sync_rate", "stage", "create_time", "update_time").Create(fse).Error
			if err != nil {
				j_log.LogError([]interface{}{"debug --- 5", err})
				return err
			}
			for _, module := range fse.FSModule {
				module.ID = 0
				module.ProjectID = project.ID
				module.FSID = fse.ID
				module.CreateTime = &now
				module.UpdateTime = &now
				err = tx.Select("", "code1", "code2", "code3", "project_id", "fs_id", "module_type",
					"module_name", "json_detail", "create_time", "update_time").Create(module).Error
				if err != nil {
					j_log.LogError([]interface{}{"debug --- 6", err})
					return err
				}
				itemList := make([]*model.FrameItem, 0)
				for _, item := range module.FrameItem {
					item.ID = 0
					item.ProjectID = project.ID
					item.FSID = fse.ID
					item.ModuleID = module.ID
					item.CreateTime = &now
					item.UpdateTime = &now
					itemList = append(itemList, item)
				}
				if len(itemList) > 0 {
					err = tx.Create(itemList).Error
					if err != nil {
						return err
					}
				}
			}
			j_log.LogInfo([]interface{}{"已导入自研设备:", fse.Name})
		}
		for _, group := range project.PageGroupList {
			group.ID = 0
			group.ProjectID = project.ID
			group.CreateTime = &now
			group.UpdateTime = &now
			err = tx.Select("project_id", "group_name", "create_time", "update_time").Create(group).Error
			if err != nil {
				return err
			}
			pageList := make([]*model.Page, 0)
			for _, page := range group.PageList {
				page.ID = 0
				page.ProjectID = project.ID
				page.GroupID = group.ID
				page.CreateTime = &now
				page.UpdateTime = &now
				pageList = append(pageList, page)
			}
			if len(pageList) > 0 {
				err = tx.Select("project_id", "group_id", "page_attribute", "page_name", "content",
					"content_ceshi", "canvas", "exp_data", "seriesColors", "report_type", "is_default", "origin_id",
					"create_time", "update_time").Create(&pageList).Error
				if err != nil {
					return err
				}
			}
			j_log.LogInfo([]interface{}{"已导入画面分组:", group.GroupName})
		}
		uid := 0
		for _, role := range project.AuthRoleList {
			if role.Id == 0 {
				for _, user := range role.UserList {
					uid = user.Id
				}
			} else {
				role.Id = 0
				role.ProjectID = project.ID
				role.CreateTime = &now
				role.UpdateTime = &now
				err = tx.Select("project_id", "name", "desc", "status", "route", "route_content",
					"route2", "route_content2", "page_report", "create_time", "update_time").Create(role).Error
				if err != nil {
					return err
				}
			}
			userList := make([]*model.User, 0)
			for _, user := range role.UserList {
				user.Id = 0
				user.ProjectID = project.ID
				user.Role = role.Id
				user.CreateTime = &now
				user.UpdateTime = &now
				userList = append(userList, user)
			}
			if len(userList) > 0 {
				err = tx.Select("project_id", "role", "tel", "username", "realname", "password", "gender",
					"age", "email", "avatar", "desc", "status", "login_type", "create_time", "update_time").Create(userList).Error
				if err != nil {
					return err
				}
			}
			j_log.LogInfo([]interface{}{"已导入角色:", role.Name})
		}
		atList := make([]*model.AlertType, 0)
		for _, at := range project.AlertTypeList {
			at.ID = 0
			at.ProjectID = project.ID
			at.Audio = strings.ReplaceAll(at.Audio, fmt.Sprintf("/%d/", rawProjectID), fmt.Sprintf("/%d/", project.ID))
			atList = append(atList, at)
		}
		if len(atList) > 0 {
			err = tx.Create(atList).Error
			if err != nil {
				return err
			}
			j_log.LogInfo([]interface{}{"已导入报警设置信息:", len(atList)})
		}
		err = tx.Model(&model.Project{}).Where("id = ?", project.ID).Update("uid", uid).Error
		if err != nil {
			return err
		}
		return nil
	})
	if err != nil {
		j_log.LogError([]interface{}{"导入工程数据失败", err})
		return
	}
	sourceAudioPath := filepath.Join(destDIR, "audio")
	destAudioPath := filepath.Join(global.StaticPath, "upload/audio", fmt.Sprintf("%d", project.ID))
	err = tools.CopyDir(sourceAudioPath, destAudioPath)
	if err != nil {
		j_log.LogError([]interface{}{"复制音频文件失败", err})
		return
	}
	j_log.LogInfo([]interface{}{"工程导入完成,总耗时", time.Since(startTime)})
}

func projectExport() {
	param := struct {
		Id       int    `json:"id" binding:"required"`
		Username string `json:"username" binding:"required,max=30"`
		Password string `json:"password" binding:"required,max=30"`
	}{}
	param.Id = 1
	param.Username = "admin"
	param.Password = "123456"
	project := model.ProjectExt{}
	err := model.DB.
		Preload("PPEList", func(tx *gorm.DB) *gorm.DB {
			return tx.Preload("FrameList", func(tx2 *gorm.DB) *gorm.DB {
				return tx2.Preload("FrameItem")
			})
		}).
		Preload("FSEList", func(tx *gorm.DB) *gorm.DB {
			return tx.Preload("FSModule", func(tx2 *gorm.DB) *gorm.DB {
				return tx2.Preload("FrameItem")
			})
		}).
		Where("id = ? AND del = 0", param.Id).Limit(1).Find(&project).Error
	if err != nil {
		j_log.LogError([]interface{}{"sql error", err})
		return
	}
	if project.ID == 0 {
		j_log.LogError([]interface{}{"无效的工程ID"})
		return
	}
	pageGroupList := make([]*model.PageGroup, 0)
	err = model.DB.Preload("PageList", func(db *gorm.DB) *gorm.DB {
		return db.Where("del = 0").Order("id ASC")
	}).Order("id ASC").Find(&pageGroupList).Error
	if err != nil {
		j_log.LogError([]interface{}{"sql error", err})
		return
	}
	userList := make([]*model.User, 0)
	err = model.DB.Where("project_id = ?", project.ID).Order("id ASC").Find(&userList).Error
	if err != nil {
		j_log.LogError([]interface{}{"sql error", err})
		return
	}
	authRoleList := make([]*model.AuthRole, 0)
	err = model.DB.Where("project_id = ?", project.ID).Order("id ASC").Find(&authRoleList).Error
	if err != nil {
		j_log.LogError([]interface{}{"sql error", err})
		return
	}
	authRoleList = append(authRoleList, &model.AuthRole{Id: 0})
	for _, role := range authRoleList {
		role.UserList = make([]*model.User, 0)
		for _, user := range userList {
			if user.Role == role.Id {
				role.UserList = append(role.UserList, user)
			}
		}
	}

	alertTypeList := make([]*model.AlertType, 0)
	err = model.DB.Where("project_id = ?", project.ID).Order("id ASC").Find(&alertTypeList).Error
	if err != nil {
		j_log.LogError([]interface{}{"sql error", err})
		return
	}

	project.AuthRoleList = authRoleList
	project.PageGroupList = pageGroupList
	project.AlertTypeList = alertTypeList

	project.ApiURL = fmt.Sprintf("http://%s:%d/", project.ServerIP, project.ServerPort)
	addr := global.AppConfig.Section("websocket").Key("addr").String()
	project.WsURL = fmt.Sprintf("ws://%s%s/echo", project.ServerIP, addr)
	jsonB, _ := json.MarshalIndent(&project, "", "  ")
	projectDirName := fmt.Sprintf("tmp/%d", project.ID)
	_ = os.RemoveAll(filepath.Join(global.StaticPath, projectDirName))
	//zipFileName := fmt.Sprintf("%s-%s", project.Name, time.Now().Format("20060102150405"))
	zipFileName := projectDirName + ".zip"
	jsonFileURL := fmt.Sprintf("%s/project.zt", projectDirName)
	jsonDestFile := filepath.Join(global.StaticPath, jsonFileURL)
	err = tools.WriteStringToFile(string(jsonB), jsonDestFile, "w")
	if err != nil {
		j_log.LogError([]interface{}{"write file error", err})
		return
	}
	err = tools.CopyDir(filepath.Join(global.StaticPath, fmt.Sprintf("upload/audio/%d", project.ID)), filepath.Join(global.StaticPath, projectDirName, "audio"))
	if err != nil {
		j_log.LogError([]interface{}{"copyDir error", err})
		return
	}
	zipDestFile := filepath.Join(global.StaticPath, filepath.Join("tmp", tools.GetNano()+".zip"))
	err = tools.ZipCompressWithoutTopLevel(filepath.Join(global.StaticPath, projectDirName), zipDestFile)
	err = tools.ZipCompressWithoutTopLevel(filepath.Join(global.StaticPath, projectDirName), zipDestFile)
	if err != nil {
		j_log.LogError([]interface{}{"zipCompress error", err})
		return
	}
	j_log.LogInfo([]interface{}{"导出成功", zipFileName})
}

// 4920616D20616C697665
